<!doctype html>
<html class="no-js" lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>
    
  OC 多线程之 GCD知识从基础到进阶 （1） - kaelinda
  
  </title>
  
  
  <link href="atom.xml" rel="alternate" title="kaelinda" type="application/atom+xml">
    <link rel="stylesheet" href="asset/css/foundation.min.css" />
    <link rel="stylesheet" href="asset/css/docs.css" />
    <script src="asset/js/vendor/modernizr.js"></script>
    <script src="asset/js/vendor/jquery.js"></script>
  <script src="asset/highlightjs/highlight.pack.js"></script>
  <link href="asset/highlightjs/styles/github.css" media="screen, projection" rel="stylesheet" type="text/css">
  <script>hljs.initHighlightingOnLoad();</script>
<script type="text/javascript">
  function before_search(){
    var searchVal = 'site:kaelinda.top ' + document.getElementById('search_input').value;
    document.getElementById('search_q').value = searchVal;
    return true;
  }
</script>
  </head>
  <body class="antialiased hide-extras">
    
    <div class="marketing off-canvas-wrap" data-offcanvas>
      <div class="inner-wrap">


<nav class="top-bar docs-bar hide-for-small" data-topbar>


  <section class="top-bar-section">
  <div class="row">
      <div style="position: relative;width:100%;"><div style="position: absolute; width:100%;">
        <ul id="main-menu" class="left">
        
        <li id=""><a target="_self" href="index.html">最近更新</a></li>
        
        <li id=""><a target="_self" href="archives.html">归档目录</a></li>
        
        <li id="ios.css"><a target="_self" href="ios.html">iOS</a></li>
        
        <li id="swift.css"><a target="_self" href="swift.html">Swift</a></li>
        
        <li id="reactnative.css"><a target="_self" href="reactnative.html">RN</a></li>
        
        <li id="js.css"><a target="_self" href="js.html">JS</a></li>
        
        <li id="shell.css"><a target="_self" href="shell.html">shell</a></li>
        
        <li id="coding.css"><a target="_self" href="coding.html">编程</a></li>
        
        <li id="live.css"><a target="_self" href="live.html">随笔</a></li>
        
        <li id="app.css"><a target="_blank" href="app.html">APP推荐</a></li>
        
        </ul>

        <ul class="right" id="search-wrap">
          <li>
<form target="_blank" onsubmit="return before_search();" action="http://google.com/search" method="get">
    <input type="hidden" id="search_q" name="q" value="" />
    <input tabindex="1" type="search" id="search_input"  placeholder="Search"/>
</form>
</li>
          </ul>
      </div></div>
  </div>
  </section>

</nav>

        <nav class="tab-bar show-for-small">
  <a href="javascript:void(0)" class="left-off-canvas-toggle menu-icon">
    <span> &nbsp; kaelinda</span>
  </a>
</nav>

<aside class="left-off-canvas-menu">
      <ul class="off-canvas-list">
        
        <li><a target="_self" href="index.html">最近更新</a></li>
        
        <li><a target="_self" href="archives.html">归档目录</a></li>
        
        <li><a target="_self" href="ios.html">iOS</a></li>
        
        <li><a target="_self" href="swift.html">Swift</a></li>
        
        <li><a target="_self" href="reactnative.html">RN</a></li>
        
        <li><a target="_self" href="js.html">JS</a></li>
        
        <li><a target="_self" href="shell.html">shell</a></li>
        
        <li><a target="_self" href="coding.html">编程</a></li>
        
        <li><a target="_self" href="live.html">随笔</a></li>
        
        <li><a target="_blank" href="app.html">APP推荐</a></li>
        

    <li><label>Categories</label></li>

        
            <li><a href="reactnative.html">React-Native</a></li>
        
            <li><a href="ios.html">iOS</a></li>
        
            <li><a href="app.html">MacAPP</a></li>
        
            <li><a href="swift.html">Swift</a></li>
        
            <li><a href="js.html">JavaScript</a></li>
        
            <li><a href="vue.html">Vue</a></li>
        
            <li><a href="shell.html">shell</a></li>
        
            <li><a href="%E7%BD%91%E7%BB%9C.html">网络</a></li>
         

      </ul>
    </aside>

<a class="exit-off-canvas" href="#"></a>


        <section id="main-content" role="main" class="scroll-container">
        
       

 <script type="text/javascript">
  $(function(){
    $('#menu_item_index').addClass('is_active');
  });
</script>
<div class="row">
  <div class="large-8 medium-8 columns">
      <div class="markdown-body article-wrap">
       <div class="article">
          
          <h1>OC 多线程之 GCD知识从基础到进阶 （1）</h1>
     
        <div class="read-more clearfix">
          <span class="date">2018/5/12</span>

          <span>posted in&nbsp;</span> 
          
              <span class="posted-in"><a href='ios.html'>iOS</a></span>
           
         
          <span class="comments">
            

            
          </span>

        </div>
      </div><!-- article -->

      <div class="article-content">
      <h2 id="toc_0">概念认知</h2>

<p>基本概念：</p>

<ul>
<li><p>GCD 全称 (Grand Center Dispatch),翻译成通俗中文“牛B的中心调度机制”</p></li>
<li><p>线程：执行任务调度的最小单位</p></li>
<li><p>任务：就是一段代码，GCD中就是block中的内容。执行任务有两种方式：同步执行（sync）和异步执行（async）。两者的主要区别是：是否等待队列的任务执行结束，以及是否具备开启新线程的能力</p></li>
<li><p>队列：用来存放任务的线性结构，采用 FIFO（先进先出）的原则。</p></li>
</ul>

<span id="more"></span><!-- more -->

<p>进阶概念：</p>

<ul>
<li>同步:
<ul>
<li>同步添加任务到指定的队列中，在添加的任务执行结束之前，会一直等待，直到队列里面的任务完成之后再继续执行后续任务。</li>
<li>只能在当前线程中执行任务，不具备开启新线程的能力。<br/></li>
</ul></li>
<li><p>异步:</p>
<ul>
<li>异步添加任务到指定的队列中，它不会做任何等待，可以继续执行任务。</li>
<li>可以在新的线程中执行任务，具备开启新线程的能力。</li>
</ul></li>
<li><p>串行队列： 队列中的任务 按添加顺序开始执行</p></li>
<li><p>并发队列： 队列中的任务可同时开始执行，可以开辟多个线程</p></li>
<li><p>主队列（等待主线程内的任务完成之后再利用主线程执行任务）</p></li>
<li><p>全局并发队列：本质上就是一个apple默认的并发队列</p></li>
</ul>

<h2 id="toc_1">GCD 使用步骤</h2>

<ul>
<li><p>创建队列：</p>
<pre><code class="language-text">// 串行队列的创建方法
dispatch_queue_t queue = dispatch_queue_create(&quot;net.bujige.testQueue&quot;, DISPATCH_QUEUE_SERIAL);<br/>
// 并发队列的创建方法<br/>
dispatch_queue_t queue = dispatch_queue_create(&quot;net.bujige.testQueue&quot;, DISPATCH_QUEUE_CONCURRENT);<br/>
// 主队列的获取方法<br/>
dispatch_queue_t queue = dispatch_get_main_queue();<br/>
// 全局并发队列的获取方法<br/>
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
</code></pre></li>
<li><p>创建任务：</p>
<pre><code class="language-text">// 同步执行任务创建方法
dispatch_sync(queue, ^{<br/>
    // 这里放同步执行任务代码<br/>
});<br/>
// 异步执行任务创建方法<br/>
dispatch_async(queue, ^{<br/>
    // 这里放异步执行任务代码<br/>
});
</code></pre></li>
</ul>

<h2 id="toc_2">串行并发队列 + 同步异步组合</h2>

<blockquote>
<p>1.同步执行 + 并发队列<br/>
2.异步执行 + 并发队列<br/>
3.同步执行 + 串行队列<br/>
4.异步执行 + 串行队列<br/>
5.同步执行 + 主队列<br/>
6.异步执行 + 主队列</p>
</blockquote>

<p>各种组合：<br/>
<img src="http://cc.cocimg.com/api/uploads/20161030/1477828129712413.png" alt="串行并发队列+同步异步组合"/></p>

<p>问题：</p>

<ul>
<li><p>死锁：在<code>主线程</code>用<code>主队列</code>执行<code>同步任务</code>就会造成线程死锁。</p></li>
<li><p>主队列中的任务一定是在主线程中执行的</p></li>
<li><p>全局并发队列中的任务可以开辟新线程，但不一定会去开辟</p></li>
</ul>

<h2 id="toc_3">GCD 线程间通信</h2>

<pre><code class="language-text">// GCD的线程间的通信 : 常用代码
- (void)GCDDemo
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@&quot;正在努力下载... %@&quot;,[NSThread currentThread]);
        // 如果下载结束回到主线程更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@&quot;下载完成,正在更新UI... %@&quot;,[NSThread currentThread]);
        });
    });
}
</code></pre>

<h2 id="toc_4">GCD 的其他方法</h2>

<h3 id="toc_5">GCD 栅栏方法：dispatch_barrier_async</h3>

<p>对于栅栏方法的理解，我觉得下面的图片很形象：<br/>
<img src="https://upload-images.jianshu.io/upload_images/1877784-4d6d77fafd3ad007.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700" alt=""/></p>

<pre><code class="language-text">dispatch_queue_t queue = dispatch_queue_create(&quot;net.bujige.testQueue&quot;, DISPATCH_QUEUE_CONCURRENT);
    
dispatch_async(queue, ^{
    // 追加任务1
    for (int i = 0; i &lt; 2; ++i) {
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@&quot;1---%@&quot;,[NSThread currentThread]);      // 打印当前线程
    }
});
    
dispatch_barrier_async(queue, ^{
        // 追加任务 barrier
        for (int i = 0; i &lt; 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@&quot;barrier---%@&quot;,[NSThread currentThread]);// 打印当前线程
        }
    });
    
dispatch_async(queue, ^{
    // 追加任务2
    for (int i = 0; i &lt; 2; ++i) {
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@&quot;3---%@&quot;,[NSThread currentThread]);      // 打印当前线程
    }
});

</code></pre>

<blockquote>
<p>虽然是异步任务 栅栏也能把这两个任务给有先后顺序的隔离开来。</p>
</blockquote>

<h3 id="toc_6">GCD 延时执行方法：dispatch_after</h3>

<blockquote>
<p>这里需要注意的是，延迟的时间是指的 延迟将任务添加到队列中。所以这个时间并不是特别准确的</p>
</blockquote>

<pre><code class="language-text">/**
 * 延时执行方法 dispatch_after
 */
- (void)after {
    NSLog(@&quot;currentThread---%@&quot;,[NSThread currentThread]);  // 打印当前线程
    NSLog(@&quot;asyncMain---begin&quot;);
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2.0秒后异步追加任务代码到主队列，并开始执行
        NSLog(@&quot;after---%@&quot;,[NSThread currentThread]);  // 打印当前线程
    });
}
</code></pre>

<h3 id="toc_7">GCD 一次性代码（只执行一次）：dispatch_once</h3>

<pre><code class="language-text"> static dispatch_once_t onceToken;
    dispatch_once(&amp;onceToken, ^{
        // 只执行1次的代码(这里面默认是线程安全的)
    });
</code></pre>

<h3 id="toc_8">GCD 快速迭代方法：dispatch_apply</h3>

<pre><code class="language-text">dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    NSLog(@&quot;apply---begin&quot;);
    dispatch_apply(6, queue, ^(size_t index) {
        NSLog(@&quot;%zd---%@&quot;,index, [NSThread currentThread]);
    });
    NSLog(@&quot;apply---end&quot;);
</code></pre>

<h3 id="toc_9">GCD 队列组：dispatch_group</h3>

<pre><code class="language-text">NSLog(@&quot;currentThread---%@&quot;,[NSThread currentThread]);  // 打印当前线程
    NSLog(@&quot;group---begin&quot;);
    
    dispatch_group_t group =  dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务1
        for (int i = 0; i &lt; 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@&quot;1---%@&quot;,[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务2
        for (int i = 0; i &lt; 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@&quot;2---%@&quot;,[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的异步任务1、任务2都执行完毕后，回到主线程执行下边任务
        for (int i = 0; i &lt; 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@&quot;3---%@&quot;,[NSThread currentThread]);      // 打印当前线程
        }
        NSLog(@&quot;group---end&quot;);
    });
</code></pre>

<h3 id="toc_10">GCD队列组监听：dispatch_group_notify</h3>

<pre><code class="language-text">    /*
     * 业务异步串行执行，用GCD实现的四种方式
     * 1、dispatch_group_notify(group, queue, ^{ });
     * 2、dispatch_group_async(group, backQueue, ^{ });
     * 3、dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 会有线程等待阻塞当前线程
     * 4、信号量 = 0  实现；会有线程等待阻塞当前线程
     */
    
    // 被监听的group
    dispatch_group_t group = dispatch_group_create();
    // 将要执行后续block代码的队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    
    // ----------------- 监听group执行完毕 后，在queue队列执行block代码
    dispatch_group_notify(group, queue, ^{
        // 任务代码
    });
    
    
    // ---------------- 全局并发队列
    dispatch_queue_t backQueue = dispatch_get_global_queue(0, 0);
    // 向group中添加 在某个队列中执行的任务
    dispatch_group_async(group, backQueue, ^{
        // 追加的任务
    });

    /*
     * 监听group执行完毕 之后在某个队列中执行某个任务
     * 跟 向group中追加某个队列中执行的任务 也有类似作用，但是group不会有线程等待，只是会造成先后执行的效果，但是不会有线程等待
     */

    // ------------ 在此处等待group中的任务执行完毕 然后 执行wait后面的代码,线程等待会阻塞当前线程
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    
    // 创建信号量 初始信号量0 线程等待
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    // 主队列中执行异步任务
    dispatch_async(queue, ^{
        // 信号量+1
        dispatch_semaphore_signal(semaphore);
    });
    // 信号量不为0时候-1，信号量=0时候线程等待
    // 异步任务异步执行后 直接带了wait,信号量=0 ，线程等待，等异步任务完毕时 信号量+1 不为零，此时线程不再等待，开始执行下面代码，会阻塞当前线程；
    dispatch_semaphore_wait(semaphore, 1);
    
    /*
     * semaphore = 0 时候 dispatch_semaphore_wait 等待信号量！=0，
     * 跟 dispatch_group_wait 等待group执行完毕 有一点相似。
     */
</code></pre>

<h3 id="toc_11">GCD中任务增删：dispatch_group_enter、dispatch_group_leave</h3>

<pre><code class="language-text">// 向任务组添加任务
dispatch_group_enter(group);
// 将要添加的任务
dispatch_async(queue, ^{
    // 追加任务1
    [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
    NSLog(@&quot;1---%@&quot;,[NSThread currentThread]);      // 打印当前线程
    // 成对出现 任务组任务执行完毕 撤销任务
    dispatch_group_leave(group);
});
// 等待group五秒钟
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5*NSEC_PER_SEC)));
// 要么五秒内执行完毕走到这里；要么五秒后超时，group还未执行完 就走到了这里
NSLog(@&quot;等待之后&quot;);
    
// 如果只有enter 没有leave那么线程会一直存在 等待group执行完毕
</code></pre>

<h3 id="toc_12">GCD信号量：dispatch_semaphore_t</h3>

<pre><code class="language-text">
-(void)semaphoreLockTest{
    
    dispatch_semaphore_t semaphoreLock = dispatch_semaphore_create(1);
    
    // 创建两个购票的串行队列
    dispatch_queue_t queue1 = dispatch_queue_create(&quot;queue1&quot;, DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create(&quot;queue2&quot;, DISPATCH_QUEUE_SERIAL);
    
    __weak typeof(&amp;*self)weakSelf = self;
    // 第一个购票队列
    dispatch_async(queue1, ^{
        [weakSelf semaphoreGetTicket:semaphoreLock];
    });
    // 第二个购票队列
    dispatch_async(queue2, ^{
        [weakSelf semaphoreGetTicket:semaphoreLock];
    });

}

-(void)semaphoreGetTicket:(dispatch_semaphore_t)semaphore{
    while (1) {
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        if (YES) {
            // 有票
            // 模拟购票延时
            [NSThread sleepForTimeInterval:0.2];
            // 相当于解锁
            dispatch_semaphore_signal(semaphore);
        }else{
            // 无票
            // 相当于解锁
            dispatch_semaphore_signal(semaphore);
            break;
        }
        
    }
}
</code></pre>

<h2 id="toc_13">线程锁</h2>

<p><a href="https://www.jianshu.com/p/35dd92bcfe8c">iOS多线程-各种线程锁的简单介绍</a></p>

<hr/>

<p>本文参考文章：</p>

<ul>
<li><a href="https://www.jianshu.com/p/2d57c72016c6">iOS多线程：『GCD』详尽总结</a></li>
<li><a href="http://www.cocoachina.com/ios/20161031/17887.html">iOS-图文表并茂，手把手教你GCD</a></li>
</ul>


    

      </div>

      <div class="row">
        <div class="large-6 columns">
        <p class="text-left" style="padding:15px 0px;">
      
          <a href="15268827199184.html" 
          title="Previous Post: NSURLRequest">&laquo; NSURLRequest</a>
      
        </p>
        </div>
        <div class="large-6 columns">
      <p class="text-right" style="padding:15px 0px;">
      
          <a  href="15257437237715.html" 
          title="Next Post: OC中的各种遍历">OC中的各种遍历 &raquo;</a>
      
      </p>
        </div>
      </div>
      <div class="comments-wrap">
        <div class="share-comments">
          

          

          
        </div>
      </div>
    </div><!-- article-wrap -->
  </div><!-- large 8 -->




 <div class="large-4 medium-4 columns">
  <div class="hide-for-small">
    <div id="sidebar" class="sidebar">
          <div id="site-info" class="site-info">
            
                <div class="site-a-logo"><img src="https://ws1.sinaimg.cn/large/006tNc79ly1fmpk9088gsj30sg0sgn33.jpg" /></div>
            
                <h1>kaelinda</h1>
                <div class="site-des">倘若我心中的山水，你眼中能看到，我便一步一莲花祈祷</div>
                <div class="social">








<a target="_blank" class="twitter" target="_blank" href="https://twitter.com/KaelLVLinda" title="Twitter">Twitter</a>
<a target="_blank" class="github" target="_blank" href="https://github.com/Kaelzzs" title="GitHub">GitHub</a>
<a target="_blank" class="email" href="mailto:zhouzuosong_kael@163.com" title="Email">Email</a>
  <a target="_blank" class="rss" href="atom.xml" title="RSS">RSS</a>
                
              	 </div>
          	</div>

             

              <div id="site-categories" class="side-item ">
                <div class="side-header">
                  <h2>Categories</h2>
                </div>
                <div class="side-content">

      	<p class="cat-list">
        
            <a href="reactnative.html"><strong>React-Native</strong></a>
        
            <a href="ios.html"><strong>iOS</strong></a>
        
            <a href="app.html"><strong>MacAPP</strong></a>
        
            <a href="swift.html"><strong>Swift</strong></a>
        
            <a href="js.html"><strong>JavaScript</strong></a>
        
            <a href="vue.html"><strong>Vue</strong></a>
        
            <a href="shell.html"><strong>shell</strong></a>
        
            <a href="%E7%BD%91%E7%BB%9C.html"><strong>网络</strong></a>
         
        </p>


                </div>
              </div>

              <div id="site-categories" class="side-item">
                <div class="side-header">
                  <h2>Recent Posts</h2>
                </div>
                <div class="side-content">
                <ul class="posts-list">
	      
		      
			      <li class="post">
			        <a href="15523077857608.html">OC中枚举写法  以及 字符串枚举探索</a>
			      </li>
		     
		  
		      
			      <li class="post">
			        <a href="15522686353535.html">iOS 动画全解 (1)</a>
			      </li>
		     
		  
		      
			      <li class="post">
			        <a href="15501356584731.html">UITableView顶部空白的几种解决办法</a>
			      </li>
		     
		  
		      
			      <li class="post">
			        <a href="15488351784332.html">Objective-C中的字符串类型枚举 探索</a>
			      </li>
		     
		  
		      
			      <li class="post">
			        <a href="15409691048860.html">【Swift笔记】引用计数</a>
			      </li>
		     
		  
		      
		  
		      
		  
		      
		  
		      
		  
		      
		  
		      
		  
		      
		  
		      
		  
		      
		  
		      
		  
		      
		  
		      
		  
		      
		  
		      
		  
		      
		   
		  		</ul>
                </div>
              </div>
        </div><!-- sidebar -->
      </div><!-- hide for small -->
</div><!-- large 4 -->

</div><!-- row -->

 <div class="page-bottom clearfix">
  <div class="row">
   <p class="copyright">Copyright &copy; 2015
Powered by <a target="_blank" href="http://www.mweb.im">MWeb</a>,&nbsp; 
Theme used <a target="_blank" href="http://github.com">GitHub CSS</a>.</p>
  </div>
</div>

        </section>
      </div>
    </div>

  
    

    <script src="asset/js/foundation.min.js"></script>
    <script>
      $(document).foundation();
      function fixSidebarHeight(){
        var w1 = $('.markdown-body').height();
          var w2 = $('#sidebar').height();
          if (w1 > w2) { $('#sidebar').height(w1); };
      }
      $(function(){
        fixSidebarHeight();
      })
      $(window).load(function(){
          fixSidebarHeight();
      });
     
    </script>

    
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script><script type="text/x-mathjax-config">MathJax.Hub.Config({TeX: { equationNumbers: { autoNumber: "AMS" } }});</script>


  </body>
</html>
